/* Copyright (C) 2014  Jean-Sebastien Castonguay
 *
 * This file is part of TechnoBoard.
 *
 * TechnoBoard is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * TechnoBoard is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with TechnoBoard.  If not, see <http://www.gnu.org/licenses/>. */

#include "technoboard.h"
#include "driver_dac.h"
#include "system.h"

#define CS_TRIS  TRISBbits.TRISB5
#define CS_PIN   PORTBbits.RB5

typedef union {
    struct {
        uint16_t data     : 12;
        uint16_t shutdown : 1;
        uint16_t gain     : 1;
        uint16_t dontCare : 1;
        uint16_t channel  : 1;
    };
    uint16_t rawData;
} MCP4822Command;

typedef enum {
    SHUTDOWN,
    ACTIVE
} ShutdownMode;

typedef enum {
    MAX_VOLTAGE_4096_MV, // 4096 mV
    MAX_VOLTAGE_2048_MV  // 2048 mV
} Gain;


static bool dacStarted = false;


static void StartFrame() {
    CS_PIN = GPIO_LOW;
}


static void StopFrame() {
    CS_PIN = GPIO_HIGN;
}


void DAC_Start() {
    
    unsigned int config1;
    unsigned int config2;
    unsigned int config3;

    CS_TRIS = GPIO_OUTPUT;
    StopFrame();
    PPSOutput( PPS_RP5, PPS_SDO1);
    PPSOutput( PPS_RP6, PPS_SCK1OUT);

    CloseSPI1();

    ConfigIntSPI1( SPI_INT_DIS | SPI_INT_PRI_4);

//    config1 = ENABLE_SCK_PIN
//            & ENABLE_SDO_PIN
//            & SPI_MODE8_ON
//            & SPI_SMP_OFF
//            & SPI_CKE_OFF         // ?????
//            /*& SLAVE_ENABLE_ON*/
//            & CLK_POL_ACTIVE_LOW
//            & MASTER_ENABLE_ON
//            & SEC_PRESCAL_8_1/*SEC_PRESCAL_1_1*/
//            & PRI_PRESCAL_64_1/*PRI_PRESCAL_1_1*/;
//    config2 = FRAME_ENABLE_OFF
//            & FIFO_BUFFER_DISABLE;
//    config3 = SPI_ENABLE
//            & SPI_IDLE_CON
//            & SPI_RX_OVFLOW_CLR;

    config1 = ENABLE_SCK_PIN
            | ENABLE_SDO_PIN
            | SPI_MODE16_ON
            | SPI_SMP_OFF
            | SPI_CKE_OFF         // ?????
            /*| SLAVE_ENABLE_ON*/
            | CLK_POL_ACTIVE_LOW
            | MASTER_ENABLE_ON
            | SEC_PRESCAL_8_1/*SEC_PRESCAL_1_1*/
            | PRI_PRESCAL_64_1/*PRI_PRESCAL_1_1*/;
    config2 = FRAME_ENABLE_OFF;
            //| FIFO_BUFFER_DISABLE;
    config3 = SPI_ENABLE
            | SPI_IDLE_CON
            | SPI_RX_OVFLOW_CLR;


    OpenSPI1( config1, config2, config3);

    dacStarted = true;
}


bool DAC_SetRawValue(DAC_Output output, unsigned int rawValue) {

    MCP4822Command command;
    bool result;
    unsigned int dummy;

    if (dacStarted == false) {
        DAC_Start();
    }
    
    if (output < DAC_NB_OF_OUTPUT) {
        
        rawValue = MIN( rawValue, DAC_MAX_RAW_VALUE);

        command.data = rawValue;
        command.shutdown = ACTIVE;
        command.gain = MAX_VOLTAGE_4096_MV;
        command.channel = output;

        StartFrame();
        WriteSPI1( command.rawData);
        while (SPI1_Tx_Buf_Full){;}
        while (!SPI1_Rx_Buf_Full){;}
        dummy = ReadSPI1();
        StopFrame();
        result = true;
    } else {
        result = false;
    }

    return result;
}


void DAC_Stop() {
    DAC_SetRawValue( DAC_P2_1, 0);
    DAC_SetRawValue( DAC_P2_2, 0);
    CloseSPI1();
    PPSOutput( PPS_RP5, PPS_NULL);
    PPSOutput( PPS_RP6, PPS_NULL);

    dacStarted = false;
}
